import { Injectable, Logger } from '@nestjs/common'
import { JwtService } from '@nestjs/jwt'
import { ConfigService } from '@nestjs/config'
import { DatabaseService } from '../database/database.service'
import { PasswordUtil } from '../common/utils/password.util'
import { LoggerUtil } from '../common/utils/logger.util'
import { ExceptionUtil } from '../common/utils/exception.util'
import { RegisterDto, LoginDto } from '../common/dto/auth.dto'
import { UserStatus } from '../common/dto/user.dto'
import * as crypto from 'crypto'

import { AuthUser, JwtPayload } from './interfaces/jwt-payload.interface'
import { UserRole } from '@prisma/client'

interface LogContext {
  userId?: string
  sessionId?: string
  ipAddress?: string
  userAgent?: string
}

/**
 * 统一认证服务
 * 处理用户注册、登录、JWT生成、密码管理等认证相关业务逻辑
 */
@Injectable()
export class AuthService {
  private readonly logger = new Logger(AuthService.name)

  constructor(
    private readonly databaseService: DatabaseService,
    private readonly jwtService: JwtService,
    private readonly configService: ConfigService,
    private readonly passwordUtil: PasswordUtil
  ) {}

  /**
   * 用户注册
   * @param registerDto 注册数据
   * @param context 日志上下文
   * @returns 注册成功的用户信息
   */
  async register(registerDto: RegisterDto, context?: LogContext): Promise<AuthUser> {
    try {
      const { username, email, password, confirmPassword, nickname } = registerDto

      // 验证密码确认
      if (password !== confirmPassword) {
        throw ExceptionUtil.createHttpException('密码和确认密码不匹配', 400)
      }

      // 验证密码强度
      const passwordValidation = this.passwordUtil.validatePasswordStrength(password)
      if (!passwordValidation.isValid) {
        throw ExceptionUtil.createHttpException(passwordValidation.message!, 400)
      }

      // 检查用户名和邮箱是否已存在
      const existingUser = await this.databaseService.user.findFirst({
        where: {
          OR: [{ username }, { email }]
        }
      })

      if (existingUser) {
        if (existingUser.username === username) {
          throw ExceptionUtil.createHttpException('用户名已存在', 409)
        }
        if (existingUser.email === email) {
          throw ExceptionUtil.createHttpException('邮箱已被注册', 409)
        }
      }

      // 加密密码
      const hashedPassword = await this.passwordUtil.hashPassword(password)

      // 创建用户
      const user = await this.databaseService.user.create({
        data: {
          username,
          email,
          password: hashedPassword,
          // nickname: nickname || username, // 字段不存在于User模型中
          role: UserRole.USER,
          isActive: true
        },
        select: {
          id: true,
          username: true,
          email: true,
          // nickname: true, // 字段不存在于User模型中
          role: true,
          avatar: true,
          isActive: true,
          createdAt: true,
          updatedAt: true
        }
      })

      LoggerUtil.authEvent(this.logger, '用户注册', username, true, { ...context, userId: user.id })
      LoggerUtil.dbOperation(this.logger, 'CREATE', 'user', user.id, context)

      return user
    } catch (error) {
      if (error.statusCode) {
        throw error
      }
      throw ExceptionUtil.handleDatabaseError(error, '用户注册')
    }
  }

  /**
   * 用户登录
   * @param loginDto 登录数据
   * @param context 日志上下文
   * @returns 登录结果（包含token和用户信息）
   */
  async login(loginDto: LoginDto, context?: LogContext) {
    try {
      const { identifier, password, rememberMe } = loginDto

      // 验证用户凭据
      const user = await this.validateUser(identifier, password, context)
      if (!user) {
        LoggerUtil.authEvent(this.logger, '用户登录', identifier, false, context)
        throw ExceptionUtil.handleAuthError('用户名或密码错误')
      }

      // 创建会话
      const session = await this.createSession(user.id, rememberMe, context)

      // 生成JWT token
      const payload: JwtPayload = {
        sub: user.id,
        username: user.username,
        email: user.email,
        role: user.role,
        sessionId: session.id
      }

      const accessToken = this.jwtService.sign(payload, {
        expiresIn: rememberMe ? '30d' : '1d'
      })

      const refreshToken = this.jwtService.sign(payload, {
        expiresIn: '30d'
      })

      // 更新最后登录时间
      await this.databaseService.user.update({
        where: { id: user.id },
        data: { lastLoginAt: new Date() }
      })

      LoggerUtil.authEvent(this.logger, '用户登录', user.username, true, { ...context, userId: user.id })

      return {
        user,
        accessToken,
        refreshToken,
        expiresIn: rememberMe ? 30 * 24 * 60 * 60 : 24 * 60 * 60
      }
    } catch (error) {
      if (error.statusCode) {
        throw error
      }
      throw ExceptionUtil.logAndThrow(error, '用户登录', '登录失败')
    }
  }

  /**
   * 验证用户凭据
   * @param emailOrUsername 用户名或邮箱
   * @param password 密码
   * @param context 日志上下文
   * @returns 用户信息或null
   */
  async validateUser(emailOrUsername: string, password: string, context?: LogContext): Promise<AuthUser | null> {
    try {
      // 根据用户名或邮箱查找用户
      const user = await this.databaseService.user.findFirst({
        where: {
          OR: [{ username: emailOrUsername }, { email: emailOrUsername }]
        },
        select: {
          id: true,
          username: true,
          email: true,
          // nickname: true, // 字段不存在于User模型中
          password: true,
          role: true,
          avatar: true,
          isActive: true,
          createdAt: true,
          updatedAt: true
        }
      })

      if (!user) {
        return null
      }

      // 检查用户是否激活
      if (!user.isActive) {
        throw ExceptionUtil.handleAuthError('用户账户已被禁用')
      }

      // 验证密码
      const isPasswordValid = await this.passwordUtil.comparePassword(password, user.password)
      if (!isPasswordValid) {
        return null
      }

      // 移除密码字段
      const { password: _, ...result } = user
      return result
    } catch (error) {
      if (error.statusCode) {
        throw error
      }
      LoggerUtil.error(this.logger, '用户验证失败', error, context)
      throw error
    }
  }

  /**
   * 创建用户会话
   * @param userId 用户ID
   * @param rememberMe 是否记住登录
   * @param context 日志上下文
   * @returns 会话信息
   */
  private async createSession(userId: string, rememberMe = false, context?: LogContext) {
    try {
      const expiresAt = new Date()
      expiresAt.setDate(expiresAt.getDate() + (rememberMe ? 30 : 1))

      const session = await this.databaseService.session.create({
        data: {
          userId,
          token: `session_${userId}_${Date.now()}`,
          expiresAt,
          userAgent: context?.userAgent || '',
          ipAddress: context?.ipAddress || ''
        }
      })

      LoggerUtil.dbOperation(this.logger, 'CREATE', 'session', session.id, context)
      return session
    } catch (error) {
      throw ExceptionUtil.handleDatabaseError(error, '创建会话')
    }
  }

  /**
   * 刷新Token
   * @param refreshToken 刷新令牌
   * @param context 日志上下文
   * @returns 新的token
   */
  async refreshToken(refreshToken: string, context?: LogContext) {
    try {
      // 验证刷新令牌
      const payload = this.jwtService.verify(refreshToken)

      // 检查会话是否存在且有效
      const session = await this.databaseService.session.findFirst({
        where: {
          token: refreshToken,
          userId: payload.sub,
          expiresAt: {
            gt: new Date()
          }
        },
        include: {
          user: true
        }
      })

      if (!session) {
        throw ExceptionUtil.handleAuthError('无效的刷新令牌')
      }

      if (!session.user.isActive) {
        throw ExceptionUtil.handleAuthError('账户已被禁用')
      }

      // 生成新的访问令牌
      const newPayload: JwtPayload = {
        sub: session.user.id,
        username: session.user.username,
        email: session.user.email,
        role: session.user.role,
        sessionId: session.id
      }

      const accessToken = this.jwtService.sign(newPayload, {
        expiresIn: '1d'
      })

      LoggerUtil.authEvent(this.logger, '令牌刷新', session.user.username, true, {
        ...context,
        userId: session.user.id
      })

      return {
        accessToken,
        expiresIn: 24 * 60 * 60
      }
    } catch (error) {
      if (error.statusCode) {
        throw error
      }
      throw ExceptionUtil.logAndThrow(error, '令牌刷新', '令牌刷新失败')
    }
  }

  /**
   * 用户登出
   * @param sessionId 会话ID
   * @param userId 用户ID
   * @param context 日志上下文
   */
  async logout(sessionId?: string, userId?: string, context?: LogContext): Promise<void> {
    try {
      if (sessionId) {
        await this.databaseService.session.delete({
          where: { id: sessionId }
        })
        LoggerUtil.dbOperation(this.logger, 'DELETE', 'session', sessionId, context)
      } else if (userId) {
        // 删除用户所有会话
        const result = await this.databaseService.session.deleteMany({
          where: { userId }
        })
        LoggerUtil.info(this.logger, `删除用户所有会话: ${result.count} 个`, { ...context, userId })
      }

      LoggerUtil.authEvent(this.logger, '用户登出', userId || 'unknown', true, context)
    } catch (error) {
      throw ExceptionUtil.handleDatabaseError(error, '用户登出')
    }
  }

  /**
   * 修改密码
   * @param userId 用户ID
   * @param currentPassword 当前密码
   * @param newPassword 新密码
   * @param confirmNewPassword 确认新密码
   * @param context 日志上下文
   */
  async changePassword(
    userId: string,
    currentPassword: string,
    newPassword: string,
    confirmNewPassword: string,
    context?: LogContext
  ) {
    try {
      if (newPassword !== confirmNewPassword) {
        throw ExceptionUtil.createHttpException('新密码确认不匹配', 400)
      }

      // 验证密码强度
      const passwordValidation = this.passwordUtil.validatePasswordStrength(newPassword)
      if (!passwordValidation.isValid) {
        throw ExceptionUtil.createHttpException(passwordValidation.message!, 400)
      }

      // 获取用户信息
      const user = await this.databaseService.user.findUnique({
        where: { id: userId },
        select: {
          id: true,
          username: true,
          password: true
        }
      })

      if (!user) {
        throw ExceptionUtil.createHttpException('用户不存在', 404)
      }

      // 验证当前密码
      const isCurrentPasswordValid = await this.passwordUtil.comparePassword(currentPassword, user.password)
      if (!isCurrentPasswordValid) {
        throw ExceptionUtil.createHttpException('当前密码错误', 400)
      }

      // 检查新密码是否与当前密码相同
      const isSamePassword = await this.passwordUtil.comparePassword(newPassword, user.password)
      if (isSamePassword) {
        throw ExceptionUtil.createHttpException('新密码不能与当前密码相同', 400)
      }

      // 加密新密码
      const hashedNewPassword = await this.passwordUtil.hashPassword(newPassword)

      // 更新密码
      await this.databaseService.user.update({
        where: { id: userId },
        data: { password: hashedNewPassword }
      })

      // 删除所有会话，强制重新登录
      await this.databaseService.session.deleteMany({
        where: { userId }
      })

      LoggerUtil.authEvent(this.logger, '密码修改', user.username, true, { ...context, userId })
      LoggerUtil.dbOperation(this.logger, 'UPDATE', 'user', userId, context)

      return {
        message: '密码修改成功，请重新登录'
      }
    } catch (error) {
      if (error.statusCode) {
        throw error
      }
      throw ExceptionUtil.handleDatabaseError(error, '密码修改')
    }
  }

  /**
   * 清理过期会话
   * @param context 日志上下文
   */
  async cleanupExpiredSessions(context?: LogContext): Promise<void> {
    try {
      const result = await this.databaseService.session.deleteMany({
        where: {
          expiresAt: {
            lt: new Date()
          }
        }
      })
      LoggerUtil.info(this.logger, `清理过期会话: ${result.count} 个`, context)
    } catch (error) {
      LoggerUtil.error(this.logger, '清理过期会话失败', error, context)
    }
  }

  /**
   * 验证用户权限
   * @param userId 用户ID
   * @param context 日志上下文
   * @returns 用户信息或null
   */
  async validateUserById(userId: string, context?: LogContext): Promise<AuthUser | null> {
    try {
      const user = await this.databaseService.user.findUnique({
        where: { id: userId },
        select: {
          id: true,
          username: true,
          email: true,
          // nickname: true, // 字段不存在于User模型中
          role: true,
          avatar: true,
          isActive: true,
          createdAt: true,
          updatedAt: true
        }
      })

      if (!user || !user.isActive) {
        return null
      }

      return user
    } catch (error) {
      LoggerUtil.error(this.logger, '验证用户失败', error, context)
      return null
    }
  }

  /**
   * 检查权限
   * @param userRole 用户角色
   * @param requiredRole 所需角色
   * @returns 是否有权限
   */
  hasPermission(userRole: string, requiredRole: string): boolean {
    const roleHierarchy = {
      'USER': 1,
      'EDITOR': 2,
      'ADMIN': 3,
      'SUPER_ADMIN': 4
    }

    return roleHierarchy[userRole] >= roleHierarchy[requiredRole]
  }

  /**
   * 忘记密码
   * @param email 邮箱
   * @param context 日志上下文
   */
  async forgotPassword(email: string, context?: LogContext): Promise<void> {
    try {
      const user = await this.databaseService.user.findUnique({
        where: { email }
      })

      if (!user) {
        // 为了安全，即使用户不存在也返回成功
        LoggerUtil.authEvent(this.logger, '忘记密码', email, false, context)
        return
      }

      // 生成重置令牌
      const resetToken = crypto.randomBytes(32).toString('hex')
      const resetTokenExpiry = new Date(Date.now() + 60 * 60 * 1000) // 1小时后过期

      // 保存重置令牌
      await this.databaseService.user.update({
        where: { id: user.id },
        data: {
          // resetToken, // 字段不存在于User模型中
          // resetTokenExpiry // 字段不存在于User模型中
        }
      })

      // TODO: 发送重置密码邮件
      LoggerUtil.authEvent(this.logger, '忘记密码', email, true, { ...context, userId: user.id })
    } catch (error) {
      throw ExceptionUtil.handleDatabaseError(error, '忘记密码')
    }
  }

  /**
   * 重置密码
   * @param token 重置令牌
   * @param newPassword 新密码
   * @param context 日志上下文
   */
  async resetPassword(token: string, newPassword: string, context?: LogContext): Promise<void> {
    try {
      const user = await this.databaseService.user.findFirst({
        where: {
          // resetToken: token, // 字段不存在于User模型中
          // resetTokenExpiry: {
          //   gt: new Date()
          // }
          id: 'temp' // TODO: 实现正确的重置令牌逻辑
        }
      })

      if (!user) {
        throw ExceptionUtil.createHttpException('无效或已过期的重置令牌', 400)
      }

      // 验证密码强度
      const passwordValidation = this.passwordUtil.validatePasswordStrength(newPassword)
      if (!passwordValidation.isValid) {
        throw ExceptionUtil.createHttpException(passwordValidation.message!, 400)
      }

      // 加密新密码
      const hashedPassword = await this.passwordUtil.hashPassword(newPassword)

      // 更新密码并清除重置令牌
      await this.databaseService.user.update({
        where: { id: user.id },
        data: {
          password: hashedPassword,
          // resetToken: null,
          // resetTokenExpiry: null // 字段不存在于User模型中
        }
      })

      // 删除所有会话
      await this.databaseService.session.deleteMany({
        where: { userId: user.id }
      })

      LoggerUtil.authEvent(this.logger, '重置密码', user.username, true, { ...context, userId: user.id })
    } catch (error) {
      if (error.statusCode) {
        throw error
      }
      throw ExceptionUtil.handleDatabaseError(error, '重置密码')
    }
  }

  /**
   * 验证邮箱
   * @param token 验证令牌
   * @param context 日志上下文
   */
  async verifyEmail(token: string, context?: LogContext) {
    try {
      // TODO: 实现邮箱验证逻辑
      const payload = this.jwtService.verify(token)
      
      const user = await this.databaseService.user.update({
        where: { id: payload.sub },
        data: {
          emailVerifiedAt: new Date()
        }
      })
      
      LoggerUtil.userAction(this.logger, '邮箱验证成功', user.email, {
        userId: payload.sub,
        ...context
      })

      return user
    } catch (error) {
      throw ExceptionUtil.handleDatabaseError(error, '验证邮箱')
    }
  }

  /**
   * 重新发送验证邮件
   * @param email 邮箱地址
   * @param context 日志上下文
   */
  async resendVerificationEmail(email: string, context: LogContext = {}): Promise<void> {
    try {
      // TODO: 实现重新发送验证邮件逻辑
      const user = await this.databaseService.user.findUnique({
        where: { email }
      })
      
      if (!user) {
        throw ExceptionUtil.createHttpException('用户不存在', 404)
      }
      
      if (user.emailVerifiedAt) {
        throw ExceptionUtil.createHttpException('邮箱已验证', 400)
      }
      
      // TODO: 发送验证邮件
      LoggerUtil.userAction(this.logger, '重新发送验证邮件', user.email, {
        userId: user.id,
        email,
        ...context
      })
    } catch (error) {
      throw ExceptionUtil.handleDatabaseError(error, '重新发送验证邮件')
    }
  }

  /**
   * 获取用户会话
   * @param userId 用户ID
   * @param context 日志上下文
   */
  async getUserSessions(userId: string, context?: LogContext) {
    try {
      const sessions = await this.databaseService.session.findMany({
        where: {
          userId,
          expiresAt: {
            gt: new Date()
          }
        },
        select: {
          id: true,
          userAgent: true,
          ipAddress: true,
          createdAt: true,
          expiresAt: true
        },
        orderBy: {
          createdAt: 'desc'
        }
      })

      return sessions
    } catch (error) {
      throw ExceptionUtil.handleDatabaseError(error, '获取用户会话')
    }
  }

  /**
   * 撤销会话
   * @param userId 用户ID
   * @param sessionId 会话ID
   * @param context 日志上下文
   */
  async revokeSession(userId: string, sessionId: string, context?: LogContext): Promise<void> {
    try {
      await this.databaseService.session.deleteMany({
        where: {
          id: sessionId,
          userId
        }
      })

      LoggerUtil.authEvent(this.logger, '撤销会话', userId, true, { ...context, sessionId })
    } catch (error) {
      throw ExceptionUtil.handleDatabaseError(error, '撤销会话')
    }
  }

  /**
   * 撤销所有会话
   * @param userId 用户ID
   * @param currentToken 当前令牌（保留）
   * @param context 日志上下文
   */
  async revokeAllSessions(userId: string, currentToken?: string, context?: LogContext): Promise<void> {
    try {
      const whereClause: any = { userId }
      
      if (currentToken) {
        // 保留当前会话
        whereClause.token = {
          not: currentToken
        }
      }

      const result = await this.databaseService.session.deleteMany({
        where: whereClause
      })

      LoggerUtil.authEvent(this.logger, '撤销所有会话', userId, true, { ...context, count: result.count })
    } catch (error) {
      throw ExceptionUtil.handleDatabaseError(error, '撤销所有会话')
    }
  }

  /**
   * 获取安全日志
   * @param userId 用户ID
   * @param options 分页选项
   * @param context 日志上下文
   */
  async getSecurityLog(userId: string, options: { page: number; limit: number }, context?: LogContext) {
    try {
      const { page, limit } = options
      const skip = (page - 1) * limit

      // 这里应该从专门的安全日志表获取，暂时返回空数组
      return {
        logs: [],
        total: 0,
        page,
        limit
      }
    } catch (error) {
      throw ExceptionUtil.handleDatabaseError(error, '获取安全日志')
    }
  }

  /**
   * 启用双因子认证
   * @param userId 用户ID
   * @param context 日志上下文
   */
  async enableTwoFactor(userId: string, context?: LogContext) {
    try {
      // TODO: 实现双因子认证逻辑
      const secret = crypto.randomBytes(32).toString('hex')
      const backupCodes = ['backup1', 'backup2', 'backup3', 'backup4', 'backup5']
      
      await this.databaseService.user.update({
        where: { id: userId },
        data: {
          twoFactorSecret: secret,
          twoFactorEnabled: true
        }
      })

      LoggerUtil.authEvent(this.logger, '启用双因子认证', userId, true, context)
      
      return {
        secret,
        qrCode: '', // TODO: 生成实际的二维码
        backupCodes
      }
    } catch (error) {
      throw ExceptionUtil.handleDatabaseError(error, '启用双因子认证')
    }
  }

  /**
   * 禁用双因子认证
   * @param userId 用户ID
   * @param code 验证码
   * @param context 日志上下文
   */
  async disableTwoFactor(userId: string, code: string, context?: LogContext): Promise<void> {
    try {
      // TODO: 验证双因子认证码
      
      await this.databaseService.user.update({
        where: { id: userId },
        data: {
          twoFactorSecret: null,
          twoFactorEnabled: false
        }
      })

      LoggerUtil.authEvent(this.logger, '禁用双因子认证', userId, true, context)
    } catch (error) {
      throw ExceptionUtil.handleDatabaseError(error, '禁用双因子认证')
    }
  }

  /**
    * 验证双因子认证
    * @param token 令牌
    * @param code 验证码
    * @param context 日志上下文
    */
   async verifyTwoFactor(token: string, code: string, context?: LogContext) {
     try {
       // TODO: 实现双因子认证验证逻辑
       const payload = this.jwtService.verify(token)
       
       const user = await this.validateUserById(payload.sub, context)
       if (!user) {
         throw ExceptionUtil.handleAuthError('用户不存在')
       }

       // 生成新的访问令牌
       const newPayload: JwtPayload = {
         sub: user.id,
         username: user.username,
         email: user.email,
         role: user.role,
         sessionId: 'temp_session'
       }

       const accessToken = this.jwtService.sign(newPayload, {
         expiresIn: '1d'
       })

       const refreshToken = this.jwtService.sign(newPayload, {
         expiresIn: '30d'
       })
       
       return {
          accessToken,
          refreshToken,
          tokenType: 'Bearer',
          expiresIn: 24 * 60 * 60,
          refreshExpiresIn: 30 * 24 * 60 * 60,
          user: {
            id: user.id,
            username: user.username,
            email: user.email,
            nickname: user.nickname || null,
            avatar: user.avatar,
            role: user.role,
            status: user.isActive ? UserStatus.ACTIVE : UserStatus.INACTIVE,
            emailVerified: user.emailVerified || false,
            twoFactorEnabled: user.twoFactorEnabled || false,
            lastLoginAt: new Date()
          }
        }
     } catch (error) {
       throw ExceptionUtil.handleDatabaseError(error, '验证双因子认证')
     }
   }

   /**
    * 获取用户资料
    * @param userId 用户ID
    * @param context 日志上下文
    */
   async getProfile(userId: string, context?: LogContext) {
     try {
       const user = await this.databaseService.user.findUnique({
         where: { id: userId },
         select: {
           id: true,
           username: true,
           email: true,
           // nickname: true, // 字段不存在于User模型中
           role: true,
           avatar: true,
           isActive: true,
           emailVerifiedAt: true,
           twoFactorEnabled: true,
           lastLoginAt: true,
           createdAt: true,
           updatedAt: true
         }
       })

       if (!user) {
         throw ExceptionUtil.createHttpException('用户不存在', 404)
       }

       return {
           ...user,
           status: user.isActive ? UserStatus.ACTIVE : UserStatus.INACTIVE
         }
     } catch (error) {
       if (error.statusCode) {
         throw error
       }
       throw ExceptionUtil.handleDatabaseError(error, '获取用户资料')
     }
   }

   /**
    * 验证令牌
    * @param token 访问令牌
    * @param context 日志上下文
    */
   async validateToken(token: string, context?: LogContext) {
     try {
       const payload = this.jwtService.verify(token)
       const user = await this.validateUserById(payload.sub, context)
       
       if (!user) {
         throw ExceptionUtil.handleAuthError('无效的令牌')
       }

       return user
     } catch (error) {
       throw ExceptionUtil.handleAuthError('令牌验证失败')
     }
   }
 }
